package info637.pprc.test;

import info637.pprc.adaptor.APIConstants;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

public class TransactionRunner extends Thread {
	
	private String serverUrl = null;
	private ObjectMapper mapper = new ObjectMapper();
	private int requestId = 0;
	
	// Processing Results
	private long transactionTime = 0; 
	private String uri = null;
	private Map<String, String> formParams = null;
	private ICompleteCallback callback = null;
	
	public TransactionRunner(String serverUrl) {
		
		this.serverUrl = serverUrl;
	}
	
	public TransactionRunner(ICompleteCallback callback, String serverUrl, String uri, Map<String, String> formParams) {
		
		this.serverUrl = serverUrl;
		this.uri = uri;
		this.formParams = formParams;
		this.callback = callback;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void run() {
		
		Map<String, Object> response = runTransaction(uri, formParams);
		
		String sessionId = null;
		
		if (response.containsKey(APIConstants.sessionId)) {
			
			sessionId = (String) response.get(APIConstants.sessionId);
		}
		
		String propertyId = null;
		
		if (response.containsKey(APIConstants.properties)) {
			
			List<Map<String, String>> properties = (List<Map<String, String>>) response.get(APIConstants.properties);
			
			if (properties.size() > 0) {
				
				propertyId = properties.get(0).get(APIConstants.propertyId);
			}
		}
		
		String version = null;
		
		if (response.containsKey(APIConstants.version)) {
			
			version = (String) response.get(APIConstants.version);
		}
		
		callback.done(this.transactionTime, isError(response), sessionId, propertyId, version);
	}
	
	protected Long getTransactionTime() {
		
		return this.transactionTime;
	}

	protected Map<String, Object> runTransaction(String uri, Map<String, String> formParams) {
		
		Map<String, Object> out = null;
		
		formParams.put(APIConstants.requestId, getRequestId());

		String response = sendHTTPPost(serverUrl + uri, formParams);
		
		if (response != null && response.trim().length() > 0) {
			
			try {
				
				out = deserializeMap(response);
						
			} catch (Exception ex) {
				
				ex.printStackTrace();
			}
			
		} else {
			
			out = new HashMap<String, Object>();
			
			out.put(APIConstants.status, "1");
		}
		
		return out;
	}    
    
    private String sendHTTPPost(String url, Map<String, String> formParams) {

		String out = null;

		HttpClient client = new HttpClient();

		PostMethod post = new PostMethod(url);

		addParams(post, formParams);

		try {

			Long startTime = System.currentTimeMillis();
			
			client.executeMethod(post);
			
			this.transactionTime = System.currentTimeMillis() - startTime;

			out = post.getResponseBodyAsString();

		} catch (Exception ex) {

			ex.printStackTrace();

		} finally {

			post.releaseConnection();
		}

		return out;
	}
    
	private boolean isError(Map<String, Object> response) {
		
		if (response.get(APIConstants.status).equals("1")) {
			
			return true;
			
		} else {
			
			return false;
		}
	}
    
	@SuppressWarnings("unchecked")
	private Map<String, Object> deserializeMap(String raw)
		throws JsonParseException, JsonMappingException, IOException {

		InputStream is = new ByteArrayInputStream(raw.getBytes("UTF-8"));

		Map<String, Object> asMap = mapper.readValue(is, HashMap.class);

		return asMap;
	}
        
	private String getRequestId() {

		requestId++;
		return Integer.toString(requestId);
	}

	private void addParams(PostMethod post, Map<String, String> params) {

		Set<String> keys = params.keySet();

		for (String key : keys) {

			String val = "";
			
			if (params.get(key) != null) {
				
				val = (String) params.get(key);
			}
			
			post.addParameter(key, val);
		}
	}
}
